package com.gushen.library.filter;

import android.content.Context;
import android.opengl.GLES20;
import com.gushen.library.utils.OpenGLUtils;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

/**
 * 抽象滤镜类，提供了渲染流程的基础框架
 */
public abstract class AbstractFilter {

    // 用于存储顶点坐标数据的缓冲区
    protected FloatBuffer mGLVertexBuffer;
    // 用于存储纹理坐标数据的缓冲区
    protected FloatBuffer mGLTextureBuffer;

    // 顶点着色器的ID
    protected String mVertexShaderId;
    // 片元着色器的ID
    protected String mFragmentShaderId;

    // OpenGL程序的ID
    protected int mGLProgramId;

    // 顶点位置属性的位置
    protected int vPosition;
    // 纹理坐标属性的位置
    protected int vCoord;

    // 变换矩阵的统一变量位置
    protected int vMatrix;

    // 纹理ID的统一变量位置
    protected int vTexture;

    // 输出的宽度和高度
    protected int mOutputWidth;
    protected int mOutputHeight;

    /**
     * 构造函数，初始化滤镜类
     *
     * @param context         上下文对象，用于访问资源
     * @param vertexShaderId  顶点着色器的资源ID
     * @param fragmentShaderId 片元着色器的资源ID
     */
    public AbstractFilter(Context context, String vertexShaderId, String fragmentShaderId) {
        this.mVertexShaderId = vertexShaderId;
        this.mFragmentShaderId = fragmentShaderId;

        //代码创建了一个顶点坐标缓冲区mGLVertexBuffer
        // 初始化顶点坐标缓冲区
        //这里的4 * 2 * 4表示分配的字节大小，其中4代表每个浮点数占用4个字节，2代表每个顶点有2个坐标（x和y），而最后的4代表有4个顶点
        // 4个点 x，y = 4*2 float 4字节 所以 4*2*4
        mGLVertexBuffer = ByteBuffer.allocateDirect(4 * 2 * 4)
                //order(ByteOrder.nativeOrder())设置了字节顺序为本地机器的顺序，这样数据就能以正确的顺序被硬件读取
                .order(ByteOrder.nativeOrder())
                //.asFloatBuffer()将字节缓冲区转换为浮点数缓冲区。
                .asFloatBuffer();

        //mGLVertexBuffer.clear()清空了缓冲区，为接下来的数据填充做准备
        mGLVertexBuffer.clear();


        /**
         * 该数组VERTEX定义了一个二维矩形的顶点坐标。具体如下：
         * 左下角：(-1.0, -1.0)
         * 右下角：(1.0, -1.0)
         * 左上角：(-1.0, 1.0)
         * 右上角：(1.0, 1.0)
         * 这些坐标用于绘制一个在二维空间中的矩形。
         */
        float[] VERTEX = {
                -1.0f, -1.0f,
                1.0f, -1.0f,
                -1.0f, 1.0f,
                1.0f, 1.0f
        };

        //mGLVertexBuffer.put(VERTEX)将这些坐标数据放入缓冲区中。
        mGLVertexBuffer.put(VERTEX);







        // 初始化纹理坐标缓冲区
        mGLTextureBuffer = ByteBuffer.allocateDirect(4 * 2 * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        mGLTextureBuffer.clear();

        /**
         * 该数组定义了一组纹理坐标（UV 坐标），用于贴图。具体包含四个顶点的 UV 坐标：
         * (0.0f, 1.0f)：左上角；
         * (1.0f, 1.0f)：右上角；
         * (0.0f, 0.0f)：左下角；
         * (1.0f, 0.0f)：右下角。 这些坐标用于在绘制时映射到相应的图像位置。
         */
        float[] TEXTURE = {
                0.0f, 1.0f,
                1.0f, 1.0f,
                0.0f, 0.0f,
                1.0f, 0.0f
        };
        mGLTextureBuffer.put(TEXTURE);

        initilize(context);
        initCoordinate();
    }

    /**
     * 初始化OpenGL ES程序，包括读取着色器文件、编译链接程序以及获取属性位置。
     *
     * @param context 上下文对象，用于访问资源
     */
    protected void initilize(Context context) {
        // 读取顶点着色器代码
//        String vertexSharder = OpenGLUtils.readRawTextFile(context, mVertexShaderId);
//        Log.e("顶点着色器代码",mVertexShaderId+"="+ vertexSharder);
        String vertexSharder = mVertexShaderId;
        // 读取片段着色器代码
//        String framentShader = OpenGLUtils.readRawTextFile(context, mFragmentShaderId);
//        Log.e("片段着色器代码",mFragmentShaderId+"="+ framentShader);
        String framentShader = mFragmentShaderId;
        // 加载并编译链接着色器程序
        mGLProgramId = OpenGLUtils.loadProgram(vertexSharder, framentShader);

        // 获取顶点位置属性的位置
        vPosition = GLES20.glGetAttribLocation(mGLProgramId, "vPosition");
        // 获取纹理坐标属性的位置
        vCoord = GLES20.glGetAttribLocation(mGLProgramId, "vCoord");
        // 获取矩阵变换属性的位置
        vMatrix = GLES20.glGetUniformLocation(mGLProgramId, "vMatrix");

        // 获取纹理采样器属性的位置
        vTexture = GLES20.glGetUniformLocation(mGLProgramId, "vTexture");
    }

    /**
     * 设置输出的宽度和高度
     *
     * @param width  输出的宽度
     * @param height 输出的高度
     */
    public void onReady(int width, int height) {
        mOutputWidth = width;
        mOutputHeight = height;
    }

    /**
     * 释放OpenGL资源
     */
    public void release() {
        GLES20.glDeleteProgram(mGLProgramId);
    }

    /**
     * 准备绘制一帧图像，设置视口大小，绑定纹理并绘制。
     *
     * @param textureId 要绘制的纹理ID
     * @return 返回绘制后的纹理ID
     */
    public int onDrawFrame(int textureId) {
        // 设置视口大小为输出图像的宽高
        GLES20.glViewport(0, 0, mOutputWidth, mOutputHeight);


        // 使用当前的着色器程序
        GLES20.glUseProgram(mGLProgramId);


        // 设置顶点位置属性指针
        mGLVertexBuffer.position(0);
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, mGLVertexBuffer);
        GLES20.glEnableVertexAttribArray(vPosition);

        // 设置纹理坐标属性指针
        mGLTextureBuffer.position(0);
        GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, mGLTextureBuffer);
        GLES20.glEnableVertexAttribArray(vCoord);


        // 激活纹理单元并绑定纹理
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
        // 设置纹理采样器
        GLES20.glUniform1i(vTexture, 0);
        // 绘制图像
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        // 解绑纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        // 返回绘制后的纹理ID
        return textureId;
    }

    /**
     * 初始化坐标系统（留作子类实现）
     */
    protected void initCoordinate() {

    }

}

